/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.core.listener;

import com.luter.heimdall.core.details.UserDetails;
import com.luter.heimdall.core.exception.HeimdallException;
import com.luter.heimdall.core.token.SimpleToken;
import com.luter.heimdall.core.utils.StrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 认证事件监听
 *
 * @author luter
 */
public abstract class AbstractAuthEvent implements AuthEventListener {
    /**
     * The constant log.
     */
    private static final transient Logger log = LoggerFactory.getLogger(AbstractAuthEvent.class);
    /**
     * The Listeners.
     */
    private Collection<AuthEventListener> listeners = new CopyOnWriteArrayList<>();

    @Override
    public String name() {
        return "";
    }

    @Override
    public void onLogin(SimpleToken token) {
        log.debug("[onLogin]::token = [{}]", token);
        for (AuthEventListener listener : this.listeners) {
            try {
                listener.onLogin(token);
                log.debug("[onLogin:({})]::  token = [{}]", listener.name(), token);
            } catch (Exception e) {
                log.error("[onLogin:({})]::Listening processing error,token = [{}],Exception = [{}]", listener.name(), token, e);
            }
        }
    }

    @Override
    public void onLogout(SimpleToken token) {
        log.debug("[onLogout]::token = [{}]", token);
        for (AuthEventListener listener : this.listeners) {
            try {
                listener.onLogout(token);
                log.debug("[onLogout:({})]::, token = [{}]", listener.name(), token);
            } catch (Exception e) {
                log.error("[onLogout:({})]::Listening processing error,token = [{}],Exception = [{}]", listener.name(), token, e);
            }
        }
    }

    @Override
    public void onTokenKick(SimpleToken token) {
        log.debug("[onTokenKick]::token = [{}]", token);
        for (AuthEventListener listener : this.listeners) {
            try {
                listener.onTokenKick(token);
                log.debug("[onTokenKick:({})]:: token = [{}]", listener.name(), token);
            } catch (Exception e) {
                log.error("[onTokenKick:({})]::Listening processing error,token = [{}],Exception = [{}]", listener.name(), token, e);
            }
        }
    }

    @Override
    public void onPrincipalKick(UserDetails userDetails, int count) {
        log.debug("[onPrincipalKick]::userDetails = [{}],count = [{}]", userDetails, count);
        for (AuthEventListener listener : this.listeners) {
            try {
                listener.onPrincipalKick(userDetails, count);
                log.debug("[onPrincipalKick:({})]::userDetails = [{}], count = [{}]", listener.name(), userDetails, count);
            } catch (Exception e) {
                log.error("[onPrincipalKick:({})]::Listening processing error,userDetails = [{}],count = [{}],Exception = [{}]",
                        listener.name(), userDetails, count, e);
            }
        }
    }

    @Override
    public void onResourceAuthorized(ResourceAuthorizedResultType result, UserDetails userDetails, String method, String url, boolean isThrowEx) {
        log.debug("[onResourceAuthorized]::type = [{}], userDetails = [{}], method = [{}], url = [{}], isThrowEx = [{}]", result, userDetails, method, url, isThrowEx);
        for (AuthEventListener listener : this.listeners) {
            try {
                listener.onResourceAuthorized(result, userDetails, method, url, isThrowEx);
                log.debug("[onResourceAuthorized::({})]::type = [{}], " +
                                "userDetails = [{}], method = [{}], url = [{}], isThrowEx = [{}]",
                        listener.name(), result, userDetails, method, url, isThrowEx);
            } catch (Exception e) {
                log.error("[onResourceAuthorized::({})]::Listening processing error. type = [{}], userDetails = [{}], method = [{}], " +
                                "url = [{}], isThrowEx = [{}], Exception = [{}]",
                        listener.name(), result, userDetails, method, url, isThrowEx, e);
            }
        }
    }

    private void addListener(AuthEventListener eventListener) {
        final boolean present = listeners.stream().anyMatch(listener -> listener.name().equals(eventListener.name()));
        if (present) {
            throw new HeimdallException("A AuthEventListener named [ " + eventListener.name() + " ]  already exists ." +
                    " Failed to add AuthEventListener. Please set a different name and try again.");
        }
        log.warn("[addListener]:: new eventListener added = [{}]", eventListener.name());
        this.getListeners().add(eventListener);
    }

    private void deleteListener(String name) {
        if (StrUtils.isNotBlank(name)) {
            final boolean b = listeners.removeIf(listener -> name.equals(listener.name()));
            log.info("[deleteListener]:: delete Listener, name = [{}], result = [{}]", name, b);
        } else {
            log.warn("[deleteListener]:: The listener is null, ignored.  name = [{}]", name);
        }
    }

    /**
     * Gets listeners.
     *
     * @return the listeners
     */
    @Override
    public Collection<AuthEventListener> getListeners() {
        return listeners;
    }

    /**
     * Sets listeners.
     *
     * @param listeners the listeners
     */
    public void setListeners(Collection<AuthEventListener> listeners) {
        this.listeners = listeners;
    }

}
